package com.jzo2o.orders.manager.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.ServeApi;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.market.dto.request.CouponUseReqDTO;
import com.jzo2o.api.market.dto.response.AvailableCouponsResDTO;
import com.jzo2o.api.market.dto.response.CouponUseResDTO;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.model.msg.TradeStatusMsg;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.NumberUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.constants.RedisConstants;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusChangeEventEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.porperties.TradeProperties;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import com.jzo2o.orders.manager.service.client.CustomerClient;
import com.jzo2o.orders.manager.service.client.MarketClient;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 下单服务类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {


    @Resource
    private CustomerClient customerClient;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ServeApi serveApi;

    @Resource
    private OrdersCreateServiceImpl owner;

    @Resource
    private NativePayApi nativePayApi;

    @Resource
    private TradingApi tradingApi;

    @Resource
    private TradeProperties tradeProperties;

    @Resource
    private OrderStateMachine orderStateMachine;

    @Resource
    private MarketClient marketClient;



    /**
     * 生成订单id 格式：{yyMMdd}{13位序号}
     *
     * @return
     */
    private Long generateOrderId() {

        //调用redis自增一个序号
        Long increment = redisTemplate.opsForValue().increment(RedisConstants.Lock.ORDERS_SHARD_KEY_ID_GENERATOR);
        //{yyMMdd}{13位序号}

        return DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd") * 10000000000000L + increment;
    }

    /**
     * 用户下单
     * @param placeOrderReqDTO
     * @return
     */
    @Override
    @GlobalTransactional
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {

        // 1.数据校验
        // 校验服务地址
        AddressBookResDTO detail = customerClient.getDetail(placeOrderReqDTO.getAddressBookId());
        if (ObjectUtil.isNull(detail)) {
            throw new BadRequestException("预约地址异常，无法下单");
        }
        // 服务
        ServeAggregationResDTO serveResDTO = serveApi.findById(placeOrderReqDTO.getServeId());
        //服务下架不可下单
        if (ObjectUtil.isNull(serveResDTO) || serveResDTO.getSaleStatus() != 2) {
            throw new BadRequestException("服务不可用");
        }


        // 2.下单前数据准备
        Orders orders = new Orders();
        // id 订单id
        orders.setId(generateOrderId());
        // userId，从threadLocal获取当前登录用户的id，通过UserContextInteceptor拦截进行设置
        orders.setUserId(UserContext.currentUserId());
        // 服务id
        orders.setServeId(placeOrderReqDTO.getServeId());
        // 服务项id
        orders.setServeItemId(serveResDTO.getServeItemId());
        orders.setServeItemName(serveResDTO.getServeItemName());
        orders.setServeItemImg(serveResDTO.getServeItemImg());
        orders.setUnit(serveResDTO.getUnit());
        //服务类型信息
        orders.setServeTypeId(serveResDTO.getServeTypeId());
        orders.setServeTypeName(serveResDTO.getServeTypeName());
        // 订单状态
        orders.setOrdersStatus(OrderStatusEnum.NO_PAY.getStatus());
        // 支付状态，暂不支持，初始化一个空状态
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        // 服务时间
        orders.setServeStartTime(placeOrderReqDTO.getServeStartTime());
        // 城市编码
        orders.setCityCode(serveResDTO.getCityCode());
        // 地理位置
        orders.setLon(detail.getLon());
        orders.setLat(detail.getLat());

        String serveAddress = new StringBuffer(detail.getProvince())
                .append(detail.getCity())
                .append(detail.getCounty())
                .append(detail.getAddress())
                .toString();
        orders.setServeAddress(serveAddress);
        // 联系人
        orders.setContactsName(detail.getName());
        orders.setContactsPhone(detail.getPhone());

        // 价格
        orders.setPrice(serveResDTO.getPrice());
        // 购买数量
        orders.setPurNum(NumberUtils.null2Default(placeOrderReqDTO.getPurNum(), 1));
        // 订单总金额 价格 * 购买数量
        orders.setTotalAmount(orders.getPrice().multiply(new BigDecimal(orders.getPurNum())));

        // 优惠金额 当前默认0
        orders.setDiscountAmount(BigDecimal.ZERO);
        // 实付金额 订单总金额 - 优惠金额
        orders.setRealPayAmount(NumberUtils.sub(orders.getTotalAmount(), orders.getDiscountAmount()));
        //排序字段,根据服务开始时间转为毫秒时间戳+订单后5位
        long sortBy = DateUtils.toEpochMilli(orders.getServeStartTime()) + orders.getId() % 100000;
        orders.setSortBy(sortBy);

        // 判断是否有用优惠劵
        if (ObjectUtil.isNotNull(placeOrderReqDTO.getCouponId())) {
            // 使用优惠劵
            owner.addWithCoupon(orders, placeOrderReqDTO.getCouponId());
        } else {
            // 不使用优惠劵
            owner.add(orders);
        }

        log.info("下单成功，订单号为：{}", orders.getId());

        //返回数据
        PlaceOrderResDTO placeOrderResDTO = new PlaceOrderResDTO();
        placeOrderResDTO.setId(orders.getId());
        return placeOrderResDTO;
    }

    /**
     * 支付
     * @param id              订单id
     * @param ordersPayReqDTO 订单支付请求体
     * @return
     */
    @Override
    public OrdersPayResDTO pay(Long id, OrdersPayReqDTO ordersPayReqDTO) {

        //查一下订单
        Orders orders = this.getById(id);

        //如果订单不存在抛出异常
        if (ObjectUtils.isNull(orders)) {
            throw new CommonException("订单不存在");
        }

        //如果订单已经支付成功直接返回
        Integer payStatus = orders.getPayStatus();//支付状态
        if (payStatus == OrderPayStatusEnum.PAY_SUCCESS.getStatus() && ObjectUtils.isNotEmpty(orders.getTradingOrderNo())) {
            OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(orders, OrdersPayResDTO.class);
            ordersPayResDTO.setProductOrderNo(orders.getId());
            return ordersPayResDTO;
        }
        // 检测订单状态是否正常   订单状态：待支付 -> 派单中
        if (ObjectUtil.notEqual(orders.getOrdersStatus(), OrderStatusEnum.NO_PAY.getStatus())) {
            // 订单状态异常 直接结束
            log.info("订单状态异常，订单号为：{}", orders.getId());
            throw new CommonException("订单状态异常");
        }
        //请求支付服务生成二维码
        NativePayResDTO nativePayResDTO = this.generateQrCode(orders, ordersPayReqDTO.getTradingChannel());
        OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(nativePayResDTO, OrdersPayResDTO.class);

        //更新订单的交易单号和支付渠道
        boolean update = this.lambdaUpdate()
                .set(Orders::getTradingOrderNo, nativePayResDTO.getTradingOrderNo())
                .set(Orders::getTradingChannel, nativePayResDTO.getTradingChannel())
                .eq(Orders::getId, id)
                .update();
        if (!update) {
            log.info("请求支付服务生成二维码，更新订单:{}的交易单号和支付渠道失败", id);
            throw new CommonException("请求支付服务生成二维码，更新订单:" + id + "的交易单号和支付渠道失败");
        }
        return ordersPayResDTO;
    }

    /**
     * 获取支付结果
     * @param id 订单id
     * @return
     */
    @Override
    public OrdersPayResDTO getPayResultFromTradServer(Long id) {
        //查一下订单
        Orders orders = this.getById(id);

        //如果订单不存在抛出异常
        if (ObjectUtils.isNull(orders)) {
            throw new CommonException("订单不存在");
        }
        //支付结果
        Integer payStatus = orders.getPayStatus();
        // 未支付且已存在支付服务的交易单号，此时远程调用支付服务查询支付结果
        if (payStatus == OrderPayStatusEnum.NO_PAY.getStatus() && ObjectUtils.isNotEmpty(orders.getTradingOrderNo())) {
            //拿到交易单号
            Long tradingOrderNo = orders.getTradingOrderNo();

            //根据交易单号请求支付服务的查询支付结果接口
            TradingResDTO tradResultByTradingOrderNo = tradingApi.findTradResultByTradingOrderNo(tradingOrderNo);
            //如果支付成功，更新数据库
            if (ObjectUtil.isNotNull(tradResultByTradingOrderNo) && ObjectUtils.equal(tradResultByTradingOrderNo.getTradingState(), TradingStateEnum.YJS)) {
                TradeStatusMsg tradeStatusMsg = new TradeStatusMsg();
                //订单id
                tradeStatusMsg.setProductOrderNo(id);
                //支付渠道
                tradeStatusMsg.setTradingChannel(tradResultByTradingOrderNo.getTradingChannel());
                //交易单号
                tradeStatusMsg.setTradingOrderNo(tradResultByTradingOrderNo.getTradingOrderNo());
                //第三方（微信）的交易单号
                tradeStatusMsg.setTransactionId(tradResultByTradingOrderNo.getTransactionId());
                //支付成功code
                tradeStatusMsg.setStatusCode(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
                //支付成功名称
                tradeStatusMsg.setStatusName(OrderPayStatusEnum.PAY_SUCCESS.name());
                //更新数据库
                owner.paySuccess(tradeStatusMsg);
                //构造返回的数据
                OrdersPayResDTO ordersPayResDTO = BeanUtils.toBean(tradeStatusMsg, OrdersPayResDTO.class);
                //支付成功
                ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
                return ordersPayResDTO;
            }
        }

        OrdersPayResDTO ordersPayResDTO = BeanUtils.copyProperties(orders, OrdersPayResDTO.class);
        ordersPayResDTO.setProductOrderNo(orders.getId());
        return ordersPayResDTO;
    }

    //sql：update orders set pay_status = ?,order_status = ? where id = ? and orders_status = 0
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(TradeStatusMsg tradeStatusMsg) {

        //支付成功更新订单表
//        boolean update = lambdaUpdate()
//                .eq(Orders::getId, tradeStatusMsg.getProductOrderNo())//订单id
//                .eq(Orders::getOrdersStatus,0)//订单状态只能由待支付变为派单中
//                .set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())//更新为支付成功
//                .set(Orders::getTransactionId, tradeStatusMsg.getTransactionId())
//                .set(Orders::getOrdersStatus, OrderStatusEnum.DISPATCHING.getStatus())//订单状态变为派单中
//                .set(Orders::getPayTime, LocalDateTime.now())
//                .update();
//        if(!update){
//            throw new CommonException("支付成功更新订单:"+tradeStatusMsg.getProductOrderNo()+"状态为派单中失败");
//        }
        //订单
        Orders orders = getById(tradeStatusMsg.getProductOrderNo());

        //使用状态机将待支付状态改为派单中
        //Long dbShardId, String bizId, StatusChangeEvent statusChangeEventEnum, T bizSnapshot
        OrderSnapshotDTO orderSnapshotDTO = new OrderSnapshotDTO();
        orderSnapshotDTO.setTradingOrderNo(tradeStatusMsg.getTradingOrderNo());//交易单号
        orderSnapshotDTO.setTradingChannel(tradeStatusMsg.getTradingChannel());//支付通道
        orderSnapshotDTO.setPayTime(LocalDateTime.now());//支付成功时间
        orderSnapshotDTO.setThirdOrderId(tradeStatusMsg.getTransactionId());//第三方支付单号
        orderStateMachine.changeStatus(orders.getUserId(), tradeStatusMsg.getProductOrderNo().toString(), OrderStatusChangeEventEnum.PAYED, orderSnapshotDTO);
    }

    @Override
    public List<Orders> queryOverTimePayOrdersListByCount(Integer count) {
        //sql：select * from orders where orders_status = 0 and create_time < (当前时间-15分钟) limit 100
        return this.lambdaQuery()
                .eq(Orders::getOrdersStatus, OrderStatusEnum.NO_PAY.getStatus())
                .lt(Orders::getCreateTime, LocalDateTime.now().minusMinutes(15))
                .last("limit " + count)
                .list();
    }

    /**
     * 请求支付服务生成二维码
     * @param orders         订单信息
     * @param tradingChannel 请求的支付渠道
     * @return
     */
    private NativePayResDTO generateQrCode(Orders orders, PayChannelEnum tradingChannel) {

        //封装请求支付服务的参数
        NativePayReqDTO nativePayDTO = new NativePayReqDTO();

        //根据请求的支付渠道需要使用哪个商户号
        Long enterpriseId = ObjectUtils.equal(tradingChannel, PayChannelEnum.WECHAT_PAY) ? tradeProperties.getWechatEnterpriseId() : tradeProperties.getAliEnterpriseId();
        nativePayDTO.setEnterpriseId(enterpriseId);
        //家政的订单号
        nativePayDTO.setProductOrderNo(orders.getId());

        //金额
        nativePayDTO.setTradingAmount(orders.getRealPayAmount());
        //业务系统标识 ，家政订单服务请求支付服务统一指定jzo2o.orders
        nativePayDTO.setProductAppId("jzo2o.orders");
        //请求的支付渠道
        nativePayDTO.setTradingChannel(tradingChannel);
        //是否切换支付渠道
        //判断如果在订单中有支付渠道并且和新传入支付渠道不一致，说明用户切换支付渠道
        if (ObjectUtils.isNotEmpty(orders.getTradingChannel()) && ObjectUtils.notEqual(orders.getTradingChannel(), tradingChannel.getValue())) {
            nativePayDTO.setChangeChannel(true);
        }
        //备注,服务 项名称
        nativePayDTO.setMemo(orders.getServeItemName());

        //请求支付服务生成二维码
        NativePayResDTO downLineTrading = nativePayApi.createDownLineTrading(nativePayDTO);
        if (ObjectUtil.isNull(downLineTrading)) {
            throw new CommonException("请求支付服务生成二维码失败");
        }

        return downLineTrading;

    }

    /**
     * 新增订单
     * @param orders
     */
    @Transactional(rollbackFor = Exception.class)
    public void add(Orders orders) {
        //插入数据库
        boolean save = this.save(orders);
        if (!save) {
            throw new CommonException("下单失败");
        }
        //调用状态机的启动方法
        //参数列表：Long dbShardId 分库分表的时候要用, String bizId 订单id, T bizSnapshot 订单快照
        OrderSnapshotDTO orderSnapshotDTO = BeanUtils.toBean(getById(orders.getId()), OrderSnapshotDTO.class);
        //分库按用户id来分，用户id % 3
        orderStateMachine.start(orders.getUserId(), orders.getId().toString(), orderSnapshotDTO);

    }

    /**
     * 获取可用优惠券
     * @param serveId
     * @param purNum
     * @return
     */
    @Override
    public List<AvailableCouponsResDTO> getAvailable(Long serveId, Integer purNum) {
        // 1.获取服务
        ServeAggregationResDTO serveAggregationResDTO = serveApi.findById(serveId);
        if (ObjectUtil.isNull(serveAggregationResDTO) || serveAggregationResDTO.getSaleStatus() != 2) {
            throw new BadRequestException("服务不可用");
        }

        // 2.计算订单总金额
        BigDecimal totalAmount = BigDecimal.valueOf(serveAggregationResDTO.getPrice().doubleValue() * purNum);

        // 3.获取可用优惠券,并返回优惠券列表
        return marketClient.getAvailable(totalAmount);
    }

    /**
     * 使用优惠券下单
     * @param orders 订单信息
     * @param couponId 优惠券id
     */
    @Override
    public void addWithCoupon(Orders orders, Long couponId) {

        CouponUseReqDTO couponUseReqDTO = new CouponUseReqDTO();
        couponUseReqDTO.setId(couponId);
        couponUseReqDTO.setOrdersId(orders.getId());
        couponUseReqDTO.setTotalAmount(orders.getTotalAmount());
        // 优惠券核销
        CouponUseResDTO couponUseResDTO = marketClient.use(couponUseReqDTO);
        orders.setDiscountAmount(couponUseResDTO.getDiscountAmount());
        // 计算实付金额
        BigDecimal realPayAmount = NumberUtil.sub(orders.getTotalAmount(), orders.getDiscountAmount());
        orders.setRealPayAmount(realPayAmount);
        // 保存订单
        owner.add(orders);
    }

}
